package com.mall.service.impl;

import cn.hutool.core.util.StrUtil;
import com.google.gson.Gson;
import com.mall.common.model.GlobalResponse;
import com.mall.dao.ProductMapper;
import com.mall.enums.GlobalResponseEnum;
import com.mall.form.CartAddForm;
import com.mall.form.CartUpdateForm;
import com.mall.pojo.Cart;
import com.mall.pojo.Product;
import com.mall.service.ICartService;
import com.mall.vo.CartProductVO;
import com.mall.vo.CartVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.mall.enums.ProductStatusEnum.ON_SALE;

/**
 * @Classname CartServiceImpl
 * @Description
 * @Date 2022/7/30 20:14
 * @Created by TaoVh
 */
@Service
public class CartServiceImpl implements ICartService {
    private static final String CART_REDIS_KEY_TEMPLATE = "cart_%d";
    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;


    private Gson gson = new Gson();


    @Override
    /*把商品加到购物车里面*/
    public GlobalResponse<CartVO> add(Integer uid, CartAddForm addForm) {
        Integer quantity = 1;
        Cart cart;

        Product product = productMapper.selectByPrimaryKey(addForm.getProductId());
        /*判断商品是否存在*/
        if (product == null) return GlobalResponse.error(GlobalResponseEnum.PRODUCT_NOT_EXIST);
        /*商品是否正常在售*/
        if (!product.getStatus().equals(ON_SALE.getCode()))
            return GlobalResponse.error(GlobalResponseEnum.PRODUCT_OFF_SALE_OR_DELETE);
        /*库存是否充足*/
        if (product.getStock() <= 0) {
            return GlobalResponse.error(GlobalResponseEnum.PRODUCT_STOCK_ERROR);
        }
        /*写入redis*/
        /*key:cart_1*/
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        String value = (String) redisTemplate.opsForHash().get(redisKey, String.valueOf(product.getId()));
        if (StrUtil.isEmptyIfStr(value)) {
            /*没有该商品，新增*/
            cart = new Cart(product.getId(), quantity, addForm.getSelected());
        } else {
            /*有该商品数量+1*/
            cart = gson.fromJson(value, Cart.class);
            cart.setQuantity(cart.getQuantity() + quantity);
        }
        redisTemplate.opsForHash().put(
                String.format(CART_REDIS_KEY_TEMPLATE, uid),
                String.valueOf(product.getId()),
                gson.toJson(cart));
        return list(uid);
    }

    @Override
    public GlobalResponse<CartVO> list(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        Map<String, String> entries = opsForHash.entries(redisKey);

        /*把涉及的product从MySQL数据库中捞出来*/
        Set<String> keySet = entries.keySet();
        Set<Integer> productIdSet = keySet.stream()
                .map(Integer::valueOf)
                .collect(Collectors.toSet());
        Map<Integer, Product> productInCartMap = this.selectProductsByProductIdSet(productIdSet);
        CartVO cartVO = new CartVO();
        List<CartProductVO> cartProductVOList = new ArrayList<>();
        boolean selectAll = true;
        Integer cartTotalQuantity = 0;
        BigDecimal cartTotalPrice = BigDecimal.ZERO;
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            Integer productId = Integer.valueOf(entry.getKey());
            Cart cart = gson.fromJson(entry.getValue(), Cart.class);
            Product product = productInCartMap.get(productId);
            if (product != null) {
                CartProductVO cartProductVO = CartProductVO.builder()
                        .productId(productId)
                        .productName(product.getName())
                        .quantity(cart.getQuantity())
                        .productMainImage(product.getMainImage())
                        .productSelected(cart.getProductSelected())
                        .productPrice(product.getPrice())
                        .productSubtitle(product.getSubtitle())
                        .productStock(product.getStock())
                        .productStatus(product.getStatus())
                        .productTotalPrice(product.getPrice().multiply(BigDecimal.valueOf(cart.getQuantity())))
                        .build();
                cartProductVOList.add(cartProductVO);

                if (!cart.getProductSelected()) {
                    selectAll = false;
                }
                /*只计算选中的总价*/
                if (cart.getProductSelected()) {
                    cartTotalPrice = cartTotalPrice.add(cartProductVO.getProductTotalPrice());
                }
            }

            cartTotalQuantity += cart.getQuantity();
        }

        cartVO.setSelectedAll(selectAll);
        cartVO.setCartTotalQuantity(cartTotalQuantity);
        cartVO.setCartTotalPrice(cartTotalPrice);
        cartVO.setCartProductVOList(cartProductVOList);
        return GlobalResponse.successByData(cartVO);
    }

    @Override
    public GlobalResponse<CartVO> update(Integer uid, Integer productId, CartUpdateForm form) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        String value = opsForHash.get(redisKey, String.valueOf(productId));
        if (StrUtil.isEmptyIfStr(value)) {
            return GlobalResponse.error(GlobalResponseEnum.PRODUCT_NOT_IN_CART);
        }
        Cart cart = gson.fromJson(value, Cart.class);
        if (form.getQuantity() != null && form.getQuantity() >= 0) cart.setQuantity(form.getQuantity());
        if (form.getSelected() != null) cart.setProductSelected(form.getSelected());
        opsForHash.put(redisKey, String.valueOf(productId), gson.toJson(cart));

        return list(uid);
    }

    @Override
    public GlobalResponse<CartVO> delete(Integer uid, Integer productId) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        String value = opsForHash.get(redisKey, String.valueOf(productId));
        if (StrUtil.isEmptyIfStr(value)) {
            return GlobalResponse.error(GlobalResponseEnum.PRODUCT_NOT_IN_CART);
        }
        opsForHash.delete(redisKey, String.valueOf(productId));
        return list(uid);
    }

    @Override
    public GlobalResponse<CartVO> selectAll(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        Map<String, String> entries = opsForHash.entries(redisKey);

        for (Map.Entry<String, String> entry : entries.entrySet()) {
            Cart cart = gson.fromJson(entry.getValue(), Cart.class);
            cart.setProductSelected(true);
            opsForHash.put(redisKey, String.valueOf(cart.getProductId())
                    , gson.toJson(cart));
        }
        return list(uid);
    }

    @Override
    public GlobalResponse<CartVO> unselectAll(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        Map<String, String> entries = opsForHash.entries(redisKey);

        for (Map.Entry<String, String> entry : entries.entrySet()) {
            Cart cart = gson.fromJson(entry.getValue(), Cart.class);
            cart.setProductSelected(false);
            opsForHash.put(redisKey, String.valueOf(cart.getProductId())
                    , gson.toJson(cart));
        }
        return list(uid);
    }

    @Override
    public GlobalResponse<Integer> sum(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        Map<String, String> entries = opsForHash.entries(redisKey);
        Integer sum = 0;
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            Cart cart = gson.fromJson(entry.getValue(), Cart.class);
            sum += cart.getQuantity();
        }
        return GlobalResponse.successByData(sum);
    }

    @Override
    public List<Cart> listForCart(Integer uid) {
        HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
        String redisKey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
        Map<String, String> entries = opsForHash.entries(redisKey);

        List<Cart> cartList = new ArrayList<>();
        for (Map.Entry<String, String> entry : entries.entrySet()) {
            cartList.add(gson.fromJson(entry.getValue(), Cart.class));
        }
        return cartList;
    }

    private Map<Integer, Product> selectProductsByProductIdSet(Set<Integer> productIdSet) {
        List<Product> products = productMapper.selectByProductIdSet(productIdSet);
        Map<Integer, Product> map = new HashMap<>();
        for (Product product : products) {
            map.put(product.getId(), product);
        }
        return map;
    }


}
